package edu.kit.ksri.db.mturk.plugins.performance;

import java.util.Date;

import org.apache.log4j.Logger;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human-based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research Institute(KSRI)
 * http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert Kern
 * 
 * ---------------------------------------------------------------------------
 * 
 * Purpose: This class fulfills the same function as the {@link TimeConstraintsManager} -
 * only that it does <b>not</b> poll MTurk for updates but rather generates
 * simulation data.
 */
public class DummyTimeConstraintsManager extends TimeConstraintsManager {
	
	private static Logger basiclog = Logger.getLogger("basic." + DummyTimeConstraintsManager.class);
	
	private static final Logger perflog = Logger.getLogger("perf." + DummyTimeConstraintsManager.class);

	/** used for worker pool simulation */
	private int activesize;
	/** used for worker pool simulation */
	private int pendingsize;
	/** used for worker pool simulation */
	private int blockedsize;
	/** used for worker pool simulation */
	private int unknownsize;

	/** how long does it take to finish one assignment */
	private int avgSecondsPerAssignment;

	/** is set to true when the run method finishes */
	private boolean done = false;

	/**
	 * 
	 * @param deadline
	 * @param forecastType
	 *            the type of forecast (e.g. linear) which shall be used
	 * @throws Exception
	 *             whenever one of the worker pools could not be found.
	 */
	public DummyTimeConstraintsManager(Date deadline,
			AbstractForecast forecastType) throws Exception {
		super(deadline, forecastType, 1000);

	}

	/**
	 * 
	 * @param dummy
	 *            the object for which a new {@link Thread} shall be started
	 * @param secondsBetweenSnapshots
	 *            how many seconds shall elapse between snapshots
	 * @param numberOfHITs
	 *            the total number of hits
	 * @param numberOfAssignmentsPerHIT
	 *            how many workers have to do the same task
	 * @param activepoolsize
	 *            size of the simulated active pool
	 * @param pendingpoolsize
	 *            size of the simulated pending pool
	 * @param blockedpoolsize
	 *            size of the simulated blocked pool
	 * @param unknownpoolsize
	 *            size of the simulated unknown pool
	 * @param avgSecondsPerAssignment
	 *            how long shall one assignment take
	 */
	public static void startSimulation(DummyTimeConstraintsManager dummy,
			int secondsBetweenSnapshots, int numberOfHITs,
			int numberOfAssignmentsPerHIT, int activepoolsize,
			int pendingpoolsize, int blockedpoolsize, int unknownpoolsize,
			int avgSecondsPerAssignment) {
		dummy.numberOfAssignments = numberOfAssignmentsPerHIT * numberOfHITs;
		dummy.secondsBetweenSnapshots = secondsBetweenSnapshots;
		dummy.activesize = activepoolsize;
		dummy.pendingsize = pendingpoolsize;
		dummy.blockedsize = blockedpoolsize;
		dummy.unknownsize = unknownpoolsize;
		dummy.avgSecondsPerAssignment = avgSecondsPerAssignment;
		new Thread(dummy).start();
	}

	@Override
	public void run() {
		boolean running = true;
		int counter;
		ProgressSnapshot lastsnapshot = new ProgressSnapshot(), newsnapshot;
		lastsnapshot.setActivePoolSize(activesize);
		lastsnapshot.setAssignmentsCompletedPerMinute(1);
		lastsnapshot.setNumberOfAssignmentsCompleted(0);
		lastsnapshot.setNumberOfDistinctWorkers(0);
		lastsnapshot.setSnapshotDate(new Date());
		synchronized (snapshots) {
			snapshots.add(lastsnapshot);
		}

		while (running) {
			// wait before polling
			try {
				Thread.sleep(1000 * secondsBetweenSnapshots);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// create new snapshot and calculate / retrieve data
			newsnapshot = new ProgressSnapshot();
			synchronized (snapshots) {
				snapshots.add(newsnapshot);
			}
			int completedSinceLastSnapshot = (int) ((Math.random() + 0.5)
					* activesize * (secondsBetweenSnapshots / avgSecondsPerAssignment));
			double perMinute = completedSinceLastSnapshot
					/ (secondsBetweenSnapshots / 60.0);
			int total = lastsnapshot.getNumberOfAssignmentsCompleted()
					+ completedSinceLastSnapshot;
			double percentage = ((100 * total) / (numberOfAssignments));
			Date snapshotDate = new Date();
			// update object
			newsnapshot.setActivePoolSize(activesize);
			newsnapshot.setAssignmentsCompletedPerMinute(perMinute);
			newsnapshot.setNumberOfAssignmentsCompleted(total);
			newsnapshot.setNumberOfDistinctWorkers(activesize); // FIXME sense?
			newsnapshot.setSnapshotDate(new Date());
			// log and invoke update for pool sizes
			perflog.info(snapshotDate + ": " + total
							+ " assignments completed ("
							+ df.format(percentage) + "%)");
			forecastType.updateActivePoolSize(this);
			lastsnapshot = newsnapshot;
			if (snapshotDate.after(deadline)
					|| ((numberOfAssignments) == total))
				break;
		}
		logSnapshotsAsCSV();
		done = true;
	}

	@Override
	public int getActivePoolSize() {
		return activesize;
	}

	@Override
	public int getBlockedPoolSize() {
		return blockedsize;
	}

	@Override
	public int getPendingPoolSize() {
		return pendingsize;
	}

	@Override
	public int getUnknownPoolSize() {
		return unknownsize;
	}

	@Override
	public void updateActivePool(int newActivePoolSize) {
		if (newActivePoolSize <= 0) {
			pendingsize += activesize;
			activesize = 0;

		} else {
			// all other cases
			int diff = newActivePoolSize - activesize;
			pendingsize -= diff;
			activesize += diff;
			if (pendingsize < 0) {
				// assert non-negative pool sizes
				activesize += pendingsize;
				pendingsize = 0;
			}
		}
		basiclog.info("\tNew pool sizes: " + activesize + " (ACTIVE), "
				+ pendingsize + " (PENDING)");
	}

	/**
	 * @return the done
	 */
	public boolean isDone() {
		return this.done;
	}

}
